package com.dc.spike.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.dc.api.spike.service.ISpikeCommodityService;
import com.dc.base.BaseApiService;
import com.dc.base.BaseResponse;
import com.dc.core.token.GenerateToken;
import com.dc.spike.mapper.SeckillMapper;
import com.dc.spike.mapper.entity.SeckillEntity;
import com.dc.spike.producer.SpikeCommodityProducer;
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @author: KEYD
 * @Date: 2020/1/20 14:36
 * @Desc: ，秒杀类
 */
@RestController
@Slf4j
public class SpikeCommodityServiceImpl extends BaseApiService<JSONObject> implements ISpikeCommodityService {

    @Autowired
    private SeckillMapper seckillMapper;

    @Autowired
    private GenerateToken generateToken;

    @Autowired
    private SpikeCommodityProducer spikeCommodityProducer;

    //每秒钟往令牌桶中存放一个Token
    private RateLimiter rateLimiter = RateLimiter.create(1);

    private BaseResponse<JSONObject> spikeFallback(String phone, Long seckillId) {
        return setResultError("服务器忙,请稍后重试!");
    }

    @Override
    @Transactional
    @HystrixCommand(fallbackMethod = "spikeFallback")
    public BaseResponse<JSONObject> spike(String phone, Long seckillId) {
        //*****************限流操作*********************************************
        boolean tryAcquire = rateLimiter.tryAcquire(0, TimeUnit.SECONDS); // 0 阻塞等待时间
        if (!tryAcquire) {
            return setResultError("当前排队人数过多，请稍候重试!");
        }
        //**********************************************************************

        //1、参数验证
        if (StringUtils.isEmpty(phone)) {
            return setResultError("手机号码不能为空!");
        }
        if (seckillId == null) {
            return setResultError("商品库存id不能为空!");
        }
        //SeckillEntity seckillEntity = seckillMapper.findBySeckillId(seckillId);
        //if (seckillEntity == null) {
        //    return setResultError("商品信息不存在");
        //}
        //2、用户频率限制  放在网关中实现
        //Boolean resultNx = redisUtils.setNx(phone, seckillId + "", 10L);
        //if (!resultNx) {
        //    return setResultError("用户访问次数过多，10s后重试");
        //}
        //3、修改数据库对应的库存
        //int inventoryDeduction = seckillMapper.inventoryDeductionUnclock(seckillId); //不加乐观锁
        //Long version = seckillEntity.getVersion();
        //int inventoryDeductionClock = seckillMapper.inventoryDeductionClock(seckillId, version);
        //if (!toDaoResult(inventoryDeductionClock)) {
        //    return setResultError("亲，请稍后重试");
        //}
        ////4、添加秒杀成功的订单,不能写成同步，应该是基于MQ的异步形式
        //OrderEntity orderEntity = new OrderEntity();
        //orderEntity.setUserPhone(phone);
        //orderEntity.setSeckillId(seckillId);
        //int insertOrder = orderMapper.insertOrder(orderEntity);
        //if (!toDaoResult(insertOrder)) {
        //    return setResultError("亲，请稍后重试");
        //}

        //*********--------通过MQ + Token 的形式完成
        //2、从redis中获取对应的秒杀 token
        String seckillToken = generateToken.getListKeyToken(seckillId + "");
        if (StringUtils.isEmpty(seckillToken)) {
            log.info(">>>>>seckillId:{}，该商品已经售罄>>>>", seckillId);
            return setResultError("亲，该商品已经售空下次再来");
        }
        //3、获取到秒杀token之后，异步放入 MQ 中实现修改商品的库存。
        sendSeckillMsg(seckillId, phone);
        return setResultSuccess("正在排队中....");
    }

    //获取到秒杀Token之后，异步放入MQ中修改商品的库存。
    @Async
    private void sendSeckillMsg(Long seckillId, String phone) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("phone", phone);
        jsonObject.put("seckillId", seckillId);
        spikeCommodityProducer.send(jsonObject);  //生产者
    }


    @Override
    public BaseResponse<JSONObject> addSpikeToken(Long seckillId, Long tokenQuantity) {
        // 1.验证参数
        if (seckillId == null) {
            return setResultError("商品库存id不能为空!");
        }
        if (tokenQuantity == null) {
            return setResultError("token数量不能为空!");
        }
        SeckillEntity seckillEntity = seckillMapper.findBySeckillId(seckillId);
        if (seckillEntity == null) {
            return setResultError("商品信息不存在!");
        }
        // 2.使用多线程异步生产令牌
        createSeckillToken(seckillId, tokenQuantity);
        return setResultSuccess("令牌正在生成中...........");
    }

    @Async
    private void createSeckillToken(Long seckillId, Long tokenQuantity) {
        generateToken.createListToken("seckill_", seckillId + "", tokenQuantity);
    }

}
